﻿using System.Drawing.Drawing2D;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System;
using System.Web;
using System.Drawing.Text;

namespace Marbles.Core.Web.Images
{
    public class ClockImageHandler : HttpHandlerBase, IHttpHandler
    {


        #region Properties

        public int Width { get { return this._width; } set { this._width = value; } }

        public int Height { get { return this._height; } set { this._height = value; } }

        public Image FaceImage
        {
            get { return img; }
            set { this.img = value; }
        }

        public bool DrawNumerals
        {
            get { return drawnumerals; }
            set { drawnumerals = value; }
        }

        public SmoothingMode SmoothingMode
        {
            get { return smoothingMode; }
            set { this.smoothingMode = value; }
        }

        public TextRenderingHint TextRenderingHint
        {
            get { return this.textRenderingHint; }
            set { this.textRenderingHint = value; }
        }

        public bool DrawRim
        {
            get { return this.drawRim; }
            set { this.drawRim = value; }
        }

        public bool DrawDropShadow
        {
            get { return this.drawDropShadow; }
            set { drawDropShadow = value; }
        }

        public Color DropShadowColor
        {
            get { return this.dropShadowColor; }
            set { this.dropShadowColor = value; }
        }

        public Color SecondHandDropShadowColor
        {
            get { return this.secondHandDropShadowColor; }
            set { this.secondHandDropShadowColor = value; }
        }

        public Color MinuteHandDropShadowColor
        {
            get { return this.minuteHandDropShadowColor; }
            set { this.minuteHandDropShadowColor = value; }
        }

        public Color HourHandDropShadowColor
        {
            get { return this.hourHandDropShadowColor; }
            set { this.hourHandDropShadowColor = value; }
        }

        public Color FaceColorHigh
        {
            get { return this.faceColor1; }
            set { this.faceColor1 = value; }
        }

        public Color FaceColorLow
        {
            get { return this.faceColor2; }
            set { this.faceColor2 = value; }
        }

        public bool DrawSecondHandShadow
        {
            get { return this.drawSecondHandShadow; }
            set { this.drawSecondHandShadow = value; }
        }

        public bool DrawHourHandShadow
        {
            get { return this.drawHourHandShadow; }
            set { this.drawHourHandShadow = value; }
        }

        public bool DrawMinuteHandShadow
        {
            get { return this.drawMinuteHandShadow; }
            set { this.drawMinuteHandShadow = value; }
        }

        public Color RimColorHigh
        {
            get { return this.rimColor1; }
            set { this.rimColor1 = value; }
        }

        public Color RimColorLow
        {
            get { return this.rimColor2; }
            set { this.rimColor2 = value; }
        }

        public LinearGradientMode RimGradientMode
        {
            get { return this.faceGradientMode; }
            set { this.faceGradientMode = value; }
        }

        public LinearGradientMode FaceGradientMode
        {
            get { return this.rimGradientMode; }
            set { this.rimGradientMode = value; }
        }

        public LineCap SecondHandEndCap
        {
            get { return this.secLineEndCap; }
            set { this.secLineEndCap = value; }
        }

        public Color SecondHandColor
        {
            get { return this.secondHandColor; }
            set { this.secondHandColor = value; }
        }

        public Color NumeralColor
        {
            get { return this.numeralColor; }
            set { this.numeralColor = value; }
        }

        public Color HourHandColor
        {
            get { return this.hourHandColor; }
            set { this.hourHandColor = value; }
        }

        public Color MinuteHandColor
        {
            get { return this.minHandColor; }
            set { this.minHandColor = value; }
        }

        public bool DrawSecondHand
        {
            get { return drawSecondHand; }
            set { this.drawSecondHand = value; }
        }

        public bool DrawMinuteHand
        {
            get { return drawMinuteHand; }
            set { this.drawMinuteHand = value; }
        }

        public bool DrawHourHand
        {
            get { return drawHourHand; }
            set { this.drawHourHand = value; }
        }

        public Point DropShadowOffset
        {
            get { return dropShadowOffset; }
            set { this.dropShadowOffset = value; }
        }

        #endregion

        private bool drawnumerals = true;
        private bool drawRim = true;
        private bool drawDropShadow = true;
        private bool drawSecondHandShadow = true;
        private bool drawMinuteHandShadow = true;
        private bool drawHourHandShadow = true;
        private bool drawSecondHand = false;
        private bool drawMinuteHand = true;
        private bool drawHourHand = true;

        private Color dropShadowColor = Color.Black;

        private Color secondHandDropShadowColor = Color.Gray;
        private Color hourHandDropShadowColor = Color.Gray;
        private Color minuteHandDropShadowColor = Color.Gray;

        private Color faceColor1 = Color.RoyalBlue;
        private Color faceColor2 = Color.SkyBlue;
        private Color rimColor1 = Color.RoyalBlue;
        private Color rimColor2 = Color.SkyBlue;
        private Color numeralColor = Color.WhiteSmoke;
        private Color secondHandColor = Color.Tomato;

        private LinearGradientBrush gb;
        private SmoothingMode smoothingMode = SmoothingMode.AntiAlias;
        private TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias;
        private LineCap secLineEndCap = LineCap.Round;
        //private Point dropShadowOffset = new Point(5,5);
        private LinearGradientMode faceGradientMode = LinearGradientMode.ForwardDiagonal;
        private LinearGradientMode rimGradientMode = LinearGradientMode.BackwardDiagonal;

        private Color hourHandColor = Color.Gainsboro;
        private Color minHandColor = Color.WhiteSmoke;

        private float radius;
        private float midx;
        private float midy;
        private float y;
        private float x;
        private float fontsize;
        private Font textFont;
        private int min;
        private int hour;
        private double sec;
        private Image img;
        private int _width = 150;
        private int _height = 150;

        float minuteAngle;
        double secondAngle;
        double hourAngle;

        private Point dropShadowOffset;

        private float GetX(float deg)
        { return (float)(radius * Math.Cos((Math.PI / 180) * deg)); }

        private float GetY(float deg)
        { return (float)(radius * Math.Sin((Math.PI / 180) * deg)); }

        /// <summary>
        /// Draws analog clock on the given GDI+ Drawing surface.
        /// </summary>
        /// <param name="e">The GDI+ Drawing surface.</param>
        private void DrawClock(Graphics e)
        {
            Graphics grfx = e;
            grfx.SmoothingMode = smoothingMode;
            grfx.TextRenderingHint = textRenderingHint;
            grfx.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

            midx = this.Width / 2;
            midy = this.Height / 2;


            x = this.Width;
            y = this.Height;

            SolidBrush stringBrush = new SolidBrush(this.numeralColor);
            Pen pen = new Pen(stringBrush, 2);


            //Define rectangles inside which we will draw circles.

            Rectangle rect = new Rectangle(0 + 10, 0 + 10, (int)x - 20, (int)y - 20);
            Rectangle rectrim = new Rectangle(0 + 20, 0 + 20, (int)x - 40, (int)y - 40);

            Rectangle rectinner = new Rectangle(0 + 20, 0 + 20, (int)x - 55, (int)y - 55);
            Rectangle rectdropshadow = new Rectangle(0 + 10, 0 + 10, (int)x - 17, (int)y - 17);


            radius = rectinner.Width / 2;

            fontsize = radius / 5;
            if (fontsize > 5)
            {
                fontsize = 8;
                textFont = new Font("Microsoft Sans Serif", fontsize, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
            }
            else { drawnumerals = false; }

            //Drop Shadow
            gb = new LinearGradientBrush(rect, Color.Transparent, dropShadowColor, LinearGradientMode.BackwardDiagonal);
            rectdropshadow.Offset(dropShadowOffset);
            if (this.drawDropShadow)
                grfx.FillEllipse(gb, rectdropshadow);


            //Face
            gb = new LinearGradientBrush(rect, rimColor1, rimColor2, faceGradientMode);
            if (this.drawRim)
                grfx.FillEllipse(gb, rect);




            //Rim
            gb = new LinearGradientBrush(rect, faceColor1, faceColor2, rimGradientMode);
            grfx.FillEllipse(gb, rectrim);










            //Define a circular clip region and draw the image inside it.
            GraphicsPath path = new GraphicsPath();
            path.AddEllipse(rectrim);
            grfx.SetClip(path);

            if (this.img != null)
            {
                grfx.DrawImage(this.img, rect);
            }
            path.Dispose();

            //Reset clip region
            grfx.ResetClip();

            //			Triangular region
            //			pen.Width =2;
            //			grfx.DrawRectangle(pen, rect);
            //			grfx.DrawRectangle(pen, rectinner);
            //			grfx.DrawRectangle(pen, rectrim);
            //			grfx.DrawRectangle(pen, rectdropshadow);
            //			
            //			grfx.DrawRectangle(pen, rect);
            //			grfx.DrawEllipse(pen, rect);
            //			grfx.DrawEllipse(pen, rectinner);
            //			grfx.DrawEllipse(pen, rectrim);
            //			grfx.DrawEllipse(pen, rectdropshadow);
            //			


            //Center Point
            //grfx.DrawEllipse(pen, midx, midy ,2 ,2);

            //Define the midpoint of the control as the centre
            grfx.TranslateTransform(midx, midy);



            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;


            //Draw Numerals on the Face 
            int deg = 360 / 12;
            if (drawnumerals)
            {
                for (int i = 1; i <= 12; i++)
                {

                    grfx.DrawString(i.ToString(), textFont, stringBrush, -1 * GetX(i * deg + 90), -1 * GetY(i * deg + 90), format);
                }

            }
            format.Dispose();



            hour = DateTime.Now.Hour;
            min = DateTime.Now.Minute;
            Point centre = new Point(0, 0);

            //Draw Minute hand
            if (drawMinuteHand)
            {

                minuteAngle = (float)(2.0 * Math.PI * (min / 60.0));
                pen.EndCap = LineCap.Round;
                pen.StartCap = LineCap.RoundAnchor;
                pen.Width = (int)radius / 14;

                centre.Offset(2, 2);
                pen.Color = Color.Gray;
                Point minHandShadow = new Point((int)(radius * Math.Sin(minuteAngle)), (int)(-(radius) * Math.Cos(minuteAngle) + 2));


                if (this.drawMinuteHandShadow)
                {
                    pen.Color = minuteHandDropShadowColor;
                    grfx.DrawLine(pen, centre, minHandShadow);
                }

                centre.X = centre.Y = 0;
                pen.Color = minHandColor;
                Point minHand = new Point((int)(radius * Math.Sin(minuteAngle)), (int)(-(radius) * Math.Cos(minuteAngle)));
                grfx.DrawLine(pen, centre, minHand);
            }

            //--End Minute Hand


            // Draw Hour Hand
            if (drawHourHand)
            {
                hourAngle = 2.0 * Math.PI * (hour + min / 60.0) / 12.0;


                pen.EndCap = LineCap.Round;
                pen.StartCap = LineCap.RoundAnchor;
                pen.Width = (int)radius / 14;

                centre.X = centre.Y = 1;
                pen.Color = Color.Gray;
                Point hourHandShadow = new Point((int)((radius * Math.Sin(hourAngle) / 1.5) + 2), (int)((-(radius) * Math.Cos(hourAngle) / 1.5) + 2));

                if (this.drawHourHandShadow)
                {
                    pen.Color = hourHandDropShadowColor;
                    grfx.DrawLine(pen, centre, hourHandShadow);
                }

                centre.X = centre.Y = 0;
                pen.Color = hourHandColor;
                Point hourHand = new Point((int)(radius * Math.Sin(hourAngle) / 1.5), (int)(-(radius) * Math.Cos(hourAngle) / 1.5));
                grfx.DrawLine(pen, centre, hourHand);
            }
            //---End Hour Hand


            sec = DateTime.Now.Second;


            //Draw Sec Hand
            if (drawSecondHand)
            {
                int width = (int)radius / 25;
                pen.Width = width;
                pen.EndCap = secLineEndCap;
                pen.StartCap = LineCap.RoundAnchor;
                secondAngle = 2.0 * Math.PI * sec / 60.0;




                //Draw Second Hand Drop Shadow
                pen.Color = Color.DimGray;
                centre.X = 1;
                centre.Y = 1;

                Point secHand = new Point((int)(radius * Math.Sin(secondAngle)), (int)(-(radius) * Math.Cos(secondAngle)));
                Point secHandshadow = new Point((int)(radius * Math.Sin(secondAngle)), (int)(-(radius) * Math.Cos(secondAngle) + 2));



                if (drawSecondHandShadow)
                {
                    pen.Color = secondHandDropShadowColor;
                    grfx.DrawLine(pen, centre, secHandshadow);

                }

                centre.X = centre.Y = 0;
                pen.Color = secondHandColor;
                grfx.DrawLine(pen, centre, secHand);
            }
            pen.Dispose();


        }


        public bool IsReusable
        {
            get { return true; }
        }



        public void ProcessRequest(HttpContext context)
        {
            if (context.Request.Params["h"] != null) { this.Height = Convert.ToInt32(context.Request.Params["h"]); }
            if (context.Request.Params["w"] != null) { this.Width = Convert.ToInt32(context.Request.Params["w"]); }

            Bitmap bmp = new Bitmap(this.Width, this.Height);
            Graphics grfx = Graphics.FromImage(bmp);
            grfx.Clear(Color.Transparent);
            DrawClock(grfx);

            ImageHandler.SaveToOutputStream(context, bmp, ImageFormat.Png);            
            grfx.Dispose();
            bmp.Dispose();


        }

        public override void Register()
        {
            RegisterHttpHandler("Clock.axd", "Marbles.Core.Web.Images.ClockImageHandler", "GET,HEAD");
        }
    }
}
